package com.zxl.study.service.impl;

import com.zxl.study.entities.MetaDatabase;
import com.zxl.study.mapper.MetaDatabaseMapper;
import com.zxl.study.model.vo.*;
import com.zxl.study.service.DatabaseService;
import com.zxl.study.service.MetaDatabaseService;
import com.zxl.study.service.MetaTableService;
import com.zxl.study.support.Constants;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.data.redis.core.Cursor;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MetaDatabaseServiceImpl implements MetaDatabaseService {
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private DatabaseService databaseService;
    @Resource
    private MetaTableService metaTableService;
    @Resource
    private MetaDatabaseMapper metaDatabaseMapper;

    @Override
    public MetaDatabaseTreeAggResultVo getRemoteTablesAggTree() throws IOException {
        MetaDatabase metaDatabase = metaDatabaseMapper.selectByPrimaryKey(1);
        MetaDatabaseTreeVo metaDatabaseTreeVo = databaseService.getMetaDatabaseTablesData(metaDatabase);

        // 顶层-数据库层级
        MetaDatabaseTreeAggResultVo resultVo = new MetaDatabaseTreeAggResultVo();
        resultVo.setType(Constants.TABLE_AGG_TREE_TYPE_DATABASE);
        resultVo.setName(metaDatabaseTreeVo.getName());
        // 将同步到的表结构转成拼接树形要的结构，构建库的子级别的
        List<MetaDatabaseTreeAggVo> aggList = changeToTreeAggVo(metaDatabaseTreeVo);
        resultVo.setChildren(metaTableService.findSchemaAndInitials(aggList));
        return resultVo;
    }

    @Override
    public void deletedDatabase() {
        //1、逻辑删除数据库
        MetaDatabase metaDatabase = metaDatabaseMapper.selectByPrimaryKey(1);
        scanRedisData(metaDatabase);
    }

    @Override
    public void scanRedisData(MetaDatabase metaDatabase) {
        //2、渐进式删除redis数据
        String DELETION_PATTERN = "database::" + metaDatabase.getId() + "::*::chunk::*";
        ScanOptions options = ScanOptions.scanOptions().match(DELETION_PATTERN).count(100).build();
        Cursor<byte[]> cursor = (Cursor<byte[]>) redisTemplate.executeWithStickyConnection(redisConnection -> redisConnection.scan(options));
        while (cursor.hasNext()) {
            byte[] rawKey = cursor.next();
            String key = new String(rawKey);
            redisTemplate.delete(key);
        }
    }

    /**
     * 将原来拼接的表清单结构修改为拼接虚拟树要的结构
     *
     * @param metaDatabaseTreeVo 表清单对象
     * @return List<MetaDatabaseTreeAggVo> 拼接虚拟树要的清单（模式，字母，数量格式）
     */
    private static List<MetaDatabaseTreeAggVo> changeToTreeAggVo(MetaDatabaseTreeVo metaDatabaseTreeVo) {
        List<MetaDatabaseTreeAggVo> resultList = new ArrayList<>();
        for (MetaColumnByTableListVo metaColumnByTableListVo:metaDatabaseTreeVo.getMetaColumnByTableListVos()) {
            List<MetaColumnByTableIdVo> idVoList = metaColumnByTableListVo.getColumnList();
            if (!CollectionUtils.isEmpty(idVoList)) {
                Map<Character, Integer> headNumMap = countFirstChar(idVoList);
                // 按照首字母排序
                List<Map.Entry<Character, Integer>> sortedList = headNumMap.entrySet().stream()
                        .sorted(Map.Entry.comparingByKey())
                        .collect(Collectors.toList());
                for (Map.Entry<Character, Integer> entry : sortedList) {
                    MetaDatabaseTreeAggVo aggVo = new MetaDatabaseTreeAggVo();
                    aggVo.setInitial(entry.getKey().toString());
                    aggVo.setNums(entry.getValue());
                    if (StringUtils.isNotEmpty(metaColumnByTableListVo.getSchemaName())) {
                        aggVo.setSchemaName(metaColumnByTableListVo.getSchemaName());
                    }
                    resultList.add(aggVo);
                }
            }
        }
        return resultList;
    }

    /**
     * 根据表英文名开头统计汇总
     * @param idVoList 表集合
     * @return 开头字母，数据量map
     */
    private static Map<Character, Integer> countFirstChar(List<MetaColumnByTableIdVo> idVoList) {
        Map<Character, Integer> countMap = new HashMap<>();
        for (MetaColumnByTableIdVo vo : idVoList) {
            char firstChar = vo.getName().charAt(0);
            countMap.put(firstChar, countMap.getOrDefault(firstChar, 0) + 1);
        }
        return countMap;
    }
}
